Skip to content

send X-Deeplake-Client: hivemind/<version> on deeplake-api calls#74

Merged
kaghni merged 5 commits intomainfrom
deeplake-client-header
Apr 28, 2026
Merged

send X-Deeplake-Client: hivemind/<version> on deeplake-api calls#74
kaghni merged 5 commits intomainfrom
deeplake-client-header

Conversation

@kaghni
Copy link
Copy Markdown
Collaborator

@kaghni kaghni commented Apr 23, 2026

Summary

  • Adds X-Deeplake-Client: hivemind/<version> header on every outbound request to deeplake-api, so the backend can attribute hivemind traffic for analytics (signup source, login source, daily retention rollup).
  • New src/utils/client-header.ts with a single deeplakeClientHeader() helper. Version is injected at bundle time via an esbuild __HIVEMIND_VERSION__ define (same pattern openclaw already uses for its own version). Dev / tsx / vitest runs fall back to "dev" so nothing crashes without a bundle.
  • Applied at every fetch site: DeepLakeAPI (query + listTables), commands/auth.ts (whoami / orgs / workspaces / device-code / device-token), and both wiki-worker.ts entry points (CC + Codex).
  • Rebuilt claude-code/bundle/ and codex/bundle/ — grep confirms the version literal + header template both land in every shipped bundle.

No PostHog SDK added to hivemind. All analytics stay server-side in deeplake-api (PR activeloopai/deeplake-api#193).

Test plan

  • npm run typecheck clean
  • npm test — all 966 tests pass
  • npm run build produces bundles; grep confirms "0.6.45" + `hivemind/${pluginVersion()}` are present in CC + Codex bundles
  • Runtime intercept-fetch smoke: calling requestDeviceCode() emits the header (confirmed X-Deeplake-Client: hivemind/dev in unbundled dev; bundle path gets hivemind/0.6.45)
  • After merge of deeplake-api#193, end-to-end: hivemind login → PostHog shows login_completed with login_source: hivemind, hivemind_version: 0.6.45
  • After a hivemind session writes to its sessions table, the next 00:05 UTC rollup emits hivemind_daily_active with matching sessions_count

The deeplake-api backend reads X-Deeplake-Client to attribute traffic
for analytics (signup_source, login_source, daily retention rollup).
Without the header, hivemind requests look identical to the generic
cli_device_flow and are invisible in PostHog.

- Add src/utils/client-header.ts with deeplakeClientHeader() using
  a build-time __HIVEMIND_VERSION__ define; dev runs fall back to
  "dev" so tsx/vitest don't crash on the undefined symbol.
- Extend esbuild.config.mjs: define __HIVEMIND_VERSION__ for the CC
  and Codex bundles (openclaw already had an analogous define for
  its own version).
- Apply the header at every outbound fetch site: DeepLakeAPI (query
  + listTables), commands/auth.ts (whoami / orgs / workspaces /
  device-code / device-token), and both wiki-worker.ts entry points.
- Rebuild CC + Codex bundles so the shipped plugin carries the
  version literal baked in.

No PostHog SDK in hivemind; backend does all analytics.
@claude
Copy link
Copy Markdown

claude Bot commented Apr 23, 2026

Claude finished @kaghni's task in 59s —— View job


One inline comment: the try/catch in pluginVersion() is dead code — typeof never throws a ReferenceError in JS, so the catch block is unreachable and the comment is misleading.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Coverage Report

Scope: files changed in this PR. Enforced threshold: 90% per metric (per file via vitest.config.ts).

Status Category Percentage Covered / Total
🔴 Lines 75.42% (🎯 90%) 313 / 415
🔴 Statements 73.39% (🎯 90%) 353 / 481
🔴 Functions 68.24% (🎯 90%) 58 / 85
🔴 Branches 68.08% (🎯 90%) 177 / 260
File Coverage — 5 files changed
File Stmts Branches Functions Lines
src/commands/auth.ts 🔴 0.0% 🔴 0.0% 🔴 0.0% 🔴 0.0%
src/deeplake-api.ts 🟢 98.4% 🟢 91.6% 🟢 97.3% 🟢 99.4%
src/hooks/codex/wiki-worker.ts 🟢 98.8% 🟢 97.1% 🟢 100.0% 🟢 98.7%
src/hooks/wiki-worker.ts 🟢 98.8% 🟢 97.1% 🟢 100.0% 🟢 98.7%
src/utils/client-header.ts 🟢 100.0% 🟢 100.0% 🟢 100.0% 🟢 100.0%

Generated for commit 33eaf42.

Comment thread src/utils/client-header.ts Outdated
Comment on lines +15 to +20
function pluginVersion(): string {
try {
if (typeof __HIVEMIND_VERSION__ === "string" && __HIVEMIND_VERSION__) {
return __HIVEMIND_VERSION__;
}
} catch { /* reference error in unbundled dev → fall through */ }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try/catch around typeof __HIVEMIND_VERSION__ is unnecessary and the comment is incorrect — typeof never throws a ReferenceError in JavaScript; it specifically returns "undefined" for undeclared identifiers. The catch block will never execute. Drop the try/catch wrapper.

Suggested change
function pluginVersion(): string {
try {
if (typeof __HIVEMIND_VERSION__ === "string" && __HIVEMIND_VERSION__) {
return __HIVEMIND_VERSION__;
}
} catch { /* reference error in unbundled dev → fall through */ }
function pluginVersion(): string {
if (typeof __HIVEMIND_VERSION__ === "string" && __HIVEMIND_VERSION__) {
return __HIVEMIND_VERSION__;
}
return "dev";
}

kaghni added 4 commits April 22, 2026 22:48
…tch too

OpenClaw is the same product as hivemind analytics-wise (same install
base per user, same retention story), so it sends the same
"hivemind/<version>" value via the shared helper. Only one outbound
deeplake-api call (POST /users/me/tokens) needed the change; openclaw
already has __HIVEMIND_VERSION__ defined by esbuild so no config
changes.

openclaw/dist/ is gitignored; rebuilds at release time.
typeof on an undeclared identifier returns "undefined", it never throws
ReferenceError. The try/catch wrapper around the __HIVEMIND_VERSION__
guard was dead code (caught block could never execute). Replace with a
plain typeof check + clearer comment explaining why the guard exists.

Also rebuilds CC + Codex bundles after merging origin/main so the
shipped artifacts reflect the new dist/ output. Marketplace bundle
version stays in sync with package.json (now 0.6.48).
The post-substitution bundle had an always-true conditional:
  if ("0.6.48") { return "0.6.48"; }
Useless at runtime and confusing on read.

Root cause: source code wrote a typeof guard so vitest (where esbuild's
define hadn't run) could fall back to "dev" without a ReferenceError.
Better fix: mirror the esbuild define in vitest.config.ts so tests also
get __HIVEMIND_VERSION__ substituted (with the literal "dev"), then drop
the guard at the source. Bundles now emit a clean
  return \`hivemind/\${"0.6.48"}\`;
which becomes "hivemind/0.6.48" at runtime with no dead branches.
@kaghni kaghni merged commit 248eb97 into main Apr 28, 2026
3 checks passed
kaghni added a commit that referenced this pull request Apr 28, 2026
…ed-npx-installer

Resolves the same three conflicts as the previous main-merge:
  - package.json: keep our @deeplake/hivemind name + publishConfig/files/
    bin additions; take main's 0.6.49 version so post-merge auto-bump
    advances to 0.6.50 cleanly.
  - openclaw/package.json: same — keep scoped name, take 0.6.49.
  - package-lock.json: regenerated via 'npm install' on top of main's
    lockfile.

Side effects from main's 7 new commits (auto-merged, no manual work):
  - PR #74 (X-Deeplake-Client header): adds src/utils/client-header.ts
    and threads it through src/deeplake-api.ts, src/commands/auth.ts,
    src/hooks/wiki-worker.ts, src/hooks/codex/wiki-worker.ts, plus the
    openclaw bundle's own fetch path.
  - vitest.config.ts: per-file thresholds for client-header.ts.
  - 0.6.49 version bump propagated across plugin.json / marketplace.json
    / claude-code/.claude-plugin/plugin.json / codex/package.json /
    openclaw/openclaw.plugin.json.

Verified post-merge: npm run ci -> 1107/1107 pass; verify-install.sh ->
22/22 PASS across all 6 agents; build clean (9 CC + 8 Codex + 6 Cursor
+ 6 Hermes + 1 OpenClaw + 1 MCP + 1 CLI bundle).
kaghni added a commit that referenced this pull request Apr 28, 2026
…ation

Coverage on src/commands/auth.ts went from 0% → 100% lines / 96%
statements / 99% branches / 81% functions. The PR coverage report on
#74 was flagging auth.ts as 0/0/0/0 because the file had no direct
unit tests on main even though every CC + Codex + openclaw fetch path
goes through it.

Test scope:
- decodeJwtPayload: well-formed, malformed-segments, malformed-base64
- credential storage: load (missing / valid / corrupt), save (creates
  config dir on first write, skips when present), delete (true / false)
- API helpers (apiGet/apiPost/apiDelete) covering the new
  X-Deeplake-Client + X-Activeloop-Org-Id header injection plus !ok
  failure modes
- listOrgs / listMembers / removeMember / inviteMember / listWorkspaces
  including both {data: [...]} and bare-array response shapes
- switchOrg / switchWorkspace including the "not logged in" guard
- device flow: requestDeviceCode, pollForToken (success, pending,
  slow_down, expired_token, access_denied, unrecognized 400, non-400),
  deviceFlowLogin (orchestration + browser-open failure + deadline
  expiry + interval-fallback + linux/win32 platform branches)
- login: full single-org and multi-org orchestration plus the
  email-prefix and "unknown" userName fallbacks

Mocks node:fs / node:os / node:child_process / global.fetch only at
the boundaries — the real auth.ts logic runs unchanged. Adds the file
to vitest.config.ts per-file thresholds (90/90/80/90) so coverage
can't regress later.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants